#include "myiic.h"
#include "delay.h"
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用，未经作者许可，不得用于其它任何用途
//ALIENTEK精英STM32开发板
//IIC驱动 代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/9
//版本：V1.0
//版权所有，盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
//////////////////////////////////////////////////////////////////////////////////
 
//初始化IIC
void MY_IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB时钟
	
	printf("MY_IIC_Init()\r\n");

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB, GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 输出高
}
// 产生IIC起始信号
// 起始信号：当SCL为高期间，SDA由高到低的跳变
void MY_IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;

	delay_us(8);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(8);
	IIC_SCL=0;//钳住I2C总线，准备发送或接收数据 
}	  

//产生IIC停止信号
// SCL为高期间，SDA由低到高的跳变
void MY_IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high

 	delay_us(4);
	IIC_SCL=1; 

	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值：1，接收应答失败
//        0，接收应答成功
u8 MY_IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			printf("MY_IIC_Wait_Ack() no ACK received.\r\n");
			MY_IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void MY_IIC_Ack(void)
{
	SDA_OUT();

	IIC_SCL=0;
	IIC_SDA=0;
	delay_us(2);
	
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void MY_IIC_NAck(void)
{
	SDA_OUT();

	IIC_SCL=0;
	IIC_SDA=1;
	delay_us(2);

	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}

#define OLED_ADDRESS	0x78 //通过调整0R电阻屏可以0x78和0x7A两个地址 -- 默认0x78
// 修正后的写单个字节到指定地址的函数
void MY_I2C_WriteByteToAddr(u8 addr, u8 data)
{
    printf("MY_I2C_WriteByteToAddr() addr:0x%x,data:0x%x\r\n",addr,data);
    MY_IIC_Start();
    delay_us(5);
    printf("MY_IIC_Start sent.\r\n"); // 添加打印信息

    if (MY_I2C_Send7bitAddress(OLED_ADDRESS, 0)) {
        printf("Device address send failed, no ACK received.\r\n");
        MY_IIC_Stop();
        return;
    }
    delay_us(5);
    printf("Device address sent.\r\n"); // 添加打印信息

    // 发送设备地址（左移1位并添加写标志0）
    if (MY_IIC_Send_Byte(addr)) {
        printf("Register address send failed, no ACK received.\r\n");
        MY_IIC_Stop();
        return;
    }
    delay_us(5);
    printf("Register address sent.\r\n"); // 添加打印信息

    if (MY_IIC_Send_Byte(data)) {
        printf("Register data send failed, no ACK received.\r\n");
        MY_IIC_Stop();
        return;
    }
    delay_us(5);
    printf("Data sent.\r\n"); // 添加打印信息

    MY_IIC_Stop();
    delay_us(10);
    printf("MY_IIC_Stop sent.\r\n"); // 添加打印信息
}

/**
 * 模拟I2C发送7位设备地址（含读写方向）
 * @param addr 7位设备地址（不包含读写位）
 * @param direction I2C_Direction_Transmitter(0) 或 I2C_Direction_Receiver(1)
 * @return 返回从机应答状态（0=ACK，1=NACK）
 */
uint8_t MY_I2C_Send7bitAddress(uint8_t addr, uint8_t direction)
{
    // 组合7位地址和1位方向位
    uint8_t address = (addr << 1) | (direction & 0x01);
    
    // 发送组合后的地址字节
    return MY_IIC_Send_Byte(address);
}

//IIC发送一个字节
//返回从机有无应答
//1，有应答
//0，无应答			  
// 修正后的发送函数，返回是否收到应答
u8 MY_IIC_Send_Byte(u8 txd)
{                        
    u8 t;
	u8 ack;
    SDA_OUT();         

    IIC_SCL=0; // 拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1;    

        delay_us(2);
        IIC_SCL=1;   // 时钟高电平期间，从机读取数据
        delay_us(2); 
        IIC_SCL=0;   // 拉低时钟，准备下一位数据
        delay_us(2);
    }     
    // 发送完成后，检测从机应答
    ack = MY_IIC_Wait_Ack(); // 返回0表示收到ACK，1表示无应答
    printf("Sent byte 0x%x, ACK: %d\r\n", txd, ack);
    return ack;
}
//读1个字节，ack=1时，发送ACK，ack=0，发送nACK   
u8 MY_IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        MY_IIC_NAck();//发送nACK
    else
        MY_IIC_Ack(); //发送ACK   
    return receive;
}



























